class SqlExecute
  #定义执行队列
  @@order_queue = SizedQueue.new(3)
  
  #入口函数
  def self.start_threads
    sleep(2)
    DaemonLogger.logger.info("SqlExecute_init: begin to load threads.")
    #启动读取函数线程
    thr = Thread.new {read_orders()}
    DaemonLogger.logger.info("SqlExecute_init: loaded read_thread: #{thr}.")
    #启动执行函数线程（多个）
    1.times do |t|
      thr = Thread.new {exec_orders()}
      DaemonLogger.logger.info("SqlExecute_init: loaded No.#{t.+ 1} exec_thread:#{thr}.")
    end
    DaemonLogger.logger.info("SqlExecute_init: finished loading all threads.")
    DaemonLogger.logger.info("show_threads: #{Thread.list}.")
    ensure
      ActiveRecord::Base.connection_pool.release_connection
  end
  
  #读取函数
  def self.read_orders()
    loop {
      sleep_interval ||= 60
      ontime_orders = []
      #筛选出已审核的订单
      audited_orders = SqlOrder.where(status: 'audited').where.not(auditor: nil)
      if audited_orders.empty?
        DaemonLogger.logger.info("SqlExecute_read: no audited order was found, sleep...")
        sleep(sleep_interval)
        next
      else
        DaemonLogger.logger.info("SqlExecute_read: found audited orders.")
      end
      #从已审核的订单中筛选出到达执行时间的
      audited_orders.each { |order|
        if order.schedule.nil?
          order.schedule = Time.now
          order.save
        end
        if order.schedule < Time.now
          DaemonLogger.logger.info("SqlExecute_read: Order:#{order.id}, the ontime order was found.")
          ontime_orders << order
        end
      }
      if ontime_orders.empty?
        DaemonLogger.logger.info("SqlExecute_read: but no audited order is ontime, sleep...")
        sleep(sleep_interval)
        next
      end
      
      ontime_orders.each { |order|
        flag = false
        #查询加锁，防止被不同线程同时加入队列
        SqlOrder.transaction do
          locked_order = SqlOrder.lock.find(order.id)
          #确认取到的订单没有被其他线程修改过
          if locked_order.status == 'audited'
            locked_order.status = 'inqueue'
            locked_order.done_entries = 0
            locked_order.save
            #给到达执行时间的订单添加flag
            flag = true
            DaemonLogger.logger.info("SqlExecute_read: Order:#{order.id}, status changed to \'inqueue\'.")
          else
            DaemonLogger.logger.warn("SqlExecute_read: Order:#{order.id}, status changed by other threads, skip.")
          end
        end
        #将有flag的订单加入执行队列
        if flag
          @@order_queue << order.reload
          DaemonLogger.logger.info("SqlExecute_read: Order:#{order.id}, was pushed into queue.")
        end
      }
      sleep(sleep_interval)
    }
  end
  
  #执行函数
  def self.exec_orders()
    loop {
      #如果执行队列为空，则休眠，并重新开始循环
      if @@order_queue.empty?
        sleep(rand().*10)
        next
      end
      #从执行队列取出一个订单
      order = @@order_queue.pop
      DaemonLogger.logger.info("SqlExecute: Order:#{order.id}, was pulled from queue:#{Thread.current}.")
      #判断订单是否被临时取消
      if SqlOrder.find(order.id).status == 'cancelled'
        DaemonLogger.logger.info("SqlExecute: Order:#{order.id}, was cancelled before excute.")
        next
      end
      
      begin
        ret = nil
        system('if [ ! -x scripts/running_query.py ]; then chmod +x scripts/running_query.py; fi')
        DaemonLogger.logger.info("SqlExecute: Order:#{order.id}, called script running_query.py.")
        #调用外部脚本执行订单，限时10分钟
        begin_time = nil
        Timeout::timeout(600) do
          begin_time = Time.now
          ret = system("scripts/running_query.py #{order.id}")
        end
        if ret
          interval = (Time.now - begin_time).to_i
          DaemonLogger.logger.info("SqlExecute: Order:#{order.id}, script running_query.py returned success.")
          DaemonLogger.logger.info("SqlExecute: Order:#{order.id}, script execute during #{interval/60}m:#{interval%60}s.")
        else
          DaemonLogger.logger.error("SqlExecute: Order:#{order.id}, script running_query.py returned fail.")
        end
      rescue => e
        DaemonLogger.logger.error("SqlExecute: Order:#{order.id}, execute error: Error: #{e.message}.")
      ensure
        ActiveRecord::Base.connection_pool.release_connection
      end
      
    }
  end

end
